package com.xiaojie.log.controller;

import lombok.extern.slf4j.Slf4j;
import net.logstash.logback.encoder.org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.IntStream;

/**
 * @author wangye
 * @classname LogController
 * @date 2021/5/21 14:38
 **/
@RestController
@RequestMapping("/log")
@Slf4j
public class LogController {

    private String kibanaServerIP;
    private Integer kibanaServerPort;

    private final RestHighLevelClient client;

    public LogController(@Value("${kibana.server.ip}") String kibanaServerIP,
                         @Value("${kibana.server.port}") Integer kibanaServerPort) {
        client = new RestHighLevelClient(
                RestClient.builder(new HttpHost(kibanaServerIP, kibanaServerPort, "http")));

    }

    /**
     * 模拟生成错误日志
     *
     * @param
     * @auther wangye
     * @date 2021/5/26
     */
    @RequestMapping(value = "/send", method = RequestMethod.GET)
    public void sendLog() {
        IntStream.range(1, 10).forEach(i -> {
            try {
                int a = 1 / 0;
            } catch (Exception e) {
                log.error("五五五五", e);
                log.info("五五五五", e);
            }
        });
    }

    /**
     * 在某一个日志级别下，按照时间范围，
     * 模糊查询多字段并按照时间倒序分页查询
     *
     * @param index     索引
     * @param query     模糊查询内容
     * @param level     日志级别
     * @param from      分页开始页
     * @param size      记录数量
     * @param startTime 时间范围，起始时间
     * @param endTime   时间范围，结束时间
     * @auther wangye
     * @date 2021/5/25
     */
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public List queryLog(String index, String query, String level, Integer from, Integer size,
                         String startTime, String endTime, String ip, String appName) {
        List<String> list = new LinkedList<>();
        try {
            //根据多条件查询
            BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
            //时间范围搜索
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("time");
            if (StringUtils.isNotEmpty(startTime)) {
                rangeQueryBuilder.gte(startTime);
            }
            if (StringUtils.isNotEmpty(endTime)) {
                rangeQueryBuilder.lte(endTime);
            }

            if (StringUtils.isNotEmpty(startTime) || StringUtils.isNotEmpty(endTime))
                boolBuilder.must(rangeQueryBuilder);
            if (StringUtils.isNotEmpty(level))
                boolBuilder.must(QueryBuilders.multiMatchQuery(level, "level"));

            if (StringUtils.isNotEmpty(query)) {
                boolBuilder
                        .should(QueryBuilders.wildcardQuery("stack_trace", "*" + query + "*"))
                        .should(QueryBuilders.wildcardQuery("class", "*" + query + "*"))
                        .should(QueryBuilders.wildcardQuery("message", "*" + query + "*"))
                        .minimumShouldMatch(1);
            }

            if (StringUtils.isNotEmpty(ip)) {
                boolBuilder.must(QueryBuilders.multiMatchQuery(ip, "ip"));
            }

            if (StringUtils.isNotEmpty(appName)) {
                boolBuilder.must(QueryBuilders.multiMatchQuery(appName, "app"));
            }

            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.query(boolBuilder).sort("time", SortOrder.DESC);

            // 分页获取记录数，默认10
            if (from != null && size != null){
                sourceBuilder.from(from);
                sourceBuilder.size(size);
            }
            HighlightBuilder highlightBuilder = addHeightLight();
            //配置高亮
            sourceBuilder.highlighter(highlightBuilder);
            SearchRequest searchRequest = new SearchRequest(index);
            searchRequest.source(sourceBuilder);

            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = response.getHits();
            SearchHit[] searchHits = hits.getHits();
            Set<String> hintIdSet = new HashSet<>();
            for (SearchHit hit : searchHits) {
                Object app = hit.getSourceAsMap().get("app");
                Object stack_tr = hit.getSourceAsMap().get("stack_trace");
                Object clazz = hit.getSourceAsMap().get("class");
                Object time = hit.getSourceAsMap().get("time");
                Object repLevel = hit.getSourceAsMap().get("level");
                Object message = hit.getSourceAsMap().get("message");
                Object serviceIp = hit.getSourceAsMap().get("ip");


                //获取高亮字段
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField titleField = highlightFields.get("stack_tr");//高亮后的标题
                HighlightField contentField = highlightFields.get("clazz");//高亮后的内容
                String id = hit.getId();

                if (hintIdSet.contains(id)) {
                    continue;
                } else {
                    hintIdSet.add(id);
                }

                list.add(id);
                list.add(repLevel.toString());
                list.add(app.toString());
                list.add(serviceIp.toString());
                list.add(message.toString());
                list.add(stack_tr.toString());
                list.add(clazz.toString());
                list.add(time.toString());


                if (titleField != null) {
                    Text[] fragments = titleField.getFragments();
                    list.add(fragments[0].toString());
                }
                if (contentField != null) {
                    Text[] fragments = contentField.getFragments();
                    list.add(fragments[0].toString());
                }
                list.add("********************************************");
            }
        } catch (Exception e) {
            log.error("query log", e);
        }
        return list;
    }

    /**
     * 增加高亮
     */
    private static HighlightBuilder addHeightLight() {
        HighlightBuilder highlightBuilder = new HighlightBuilder(); //生成高亮查询器
        highlightBuilder.field("title");      //高亮查询字段
        highlightBuilder.field("content");    //高亮查询字段
        highlightBuilder.requireFieldMatch(false);     //如果要多个字段高亮,这项要为false
        highlightBuilder.preTags("<span style=\"color:red\">");   //高亮设置
        highlightBuilder.postTags("</span>");

        //下面这两项,如果你要高亮如文字内容等有很多字的字段,必须配置,不然会导致高亮不全,文章内容缺失等
        highlightBuilder.fragmentSize(800000); //最大高亮分片数
        highlightBuilder.numOfFragments(0); //从第一个分片获取高亮片段
        return highlightBuilder;
    }


}
